﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Transactions;
using Security;
using Storage;

namespace Groupware
{
    /// <summary>
    /// Provides functionalities for notification
    /// </summary>
    public static class NotificationManager
    {
        #region Methods

        /// <summary>
        /// Retrives all (but FriendshipRequests and FriendshipNotifications)
        /// notifications received by userID
        /// </summary>
        public static List<Notification> getNotifications(int userID)
        {
            List<Notification> notifications = new List<Notification>();
            List<int> notificationsID = 
                (from Storage.EntityModels.Notifications n in StorageManager.Instance.Entities.Notifications
                 where n.Receiver.Id == userID
                       && n.Message.Type != Notification.FRIEND_REQUEST
                       && n.Message.Type != Notification.FRIEND_REQUEST_ACCEPTED
                 select n.Id).ToList();
            foreach (int id in notificationsID)
            {
                Storage.EntityModels.Notifications n = Storage.StorageManager.Instance.GetNotificationById(id);
                Storage.EntityModels.Message message = Storage.StorageManager.Instance.GetMessageById(n.MessageId);
                Notification thisNotification = new Notification() 
                {
                    SenderID = n.UserId,
                    IsRead = n.IsRead,
                    TripId = message.TripId,
                    EventId = message.EventId,
                    TemplateType = message.Type,
                    Id = id
                };
                thisNotification.XmlToNotification(message.Text);
                notifications.Add(thisNotification);
            }
            
            return notifications;
        }

        /// <summary>
        /// Retrives all (but FriendshipRequests and FriendshipNotifications)
        /// notifications of one type received by userID
        /// </summary>
        public static List<Notification> getNotifications(int userID, int type)
        {
            return getNotifications(userID).Where(n => n.TemplateType == type).ToList();
        }

        /// <summary>
        /// Sends a notification of new trip created (Notification.TRIP_CREATED) to all owner's friends
        /// </summary>
        /// <param name="ownerId">id of trip's owner</param>
        /// <param name="tripId">id of new trip</param>
        /// <returns>true if there are no errors, false otherwise</returns>
        public static bool sendNewTripNotificationToFriends(int ownerId, int tripId)
        {
            Storage.EntityModels.Trip t = null;
            try {
                t = StorageManager.Instance.GetTripById(tripId);
            } catch (Storage.Exceptions.TripNotFoundException) { return false; }

            if (t.OwnerId != ownerId) return false;

            List<int> friends = StorageManager.Instance.GetFriendsByUserId(t.OwnerId.Value);
            
            Notification n = new Notification()
            {
                TemplateType = Notification.TRIP_CREATED,
                TripId = t.Id,
                SenderID = t.OwnerId
            };
            n.Store(friends);

            return true;
        }

        /// <summary>
        /// Removes all notifications received by user with 'id' userId
        /// Removes also message row if there aren't notification releated left
        /// </summary>
        /// <param name="userId">id of the User</param>
        /// <param name="onlyAlreadyRead">if true, remove only already read notifications</param>
        public static void RemoveAllNotificationsReceivedByUserId(int userId, bool onlyAlreadyRead)
        {
            using (TransactionScope scope = new TransactionScope())
            {
                var notifications =
                    from Storage.EntityModels.Notifications n in Storage.StorageManager.Instance.Entities.Notifications
                    where n.ReceiverUserId == userId
                    select n;

                if (onlyAlreadyRead)
                    notifications = notifications.Where(n => n.IsRead == true);

                foreach (var n in notifications)
                {
                    if ((from Storage.EntityModels.Notifications others
                             in Storage.StorageManager.Instance.Entities.Notifications
                         where others.MessageId == n.MessageId && others.ReceiverUserId != n.ReceiverUserId
                         select others).Count() == 0)
                        Storage.StorageManager.Instance.Entities.Messages.DeleteOnSubmit(n.Message);
                    Storage.StorageManager.Instance.Entities.Notifications.DeleteOnSubmit(n);
                }
                Storage.StorageManager.Instance.Entities.SubmitChanges();
                scope.Complete();
            }
        }

        /// <summary>
        /// Removes a List of Notifications received by user 
        /// </summary>
        /// <param name="user">a TripLoaUser</param>
        /// <param name="notificationsToRemove">A list of Notification for the given user</param>
        public static void RemoveNotificationsReceivedByUser(TripLoaUser user, List<Notification> notificationsToRemove)
        {
            using (TransactionScope scope = new TransactionScope())
            {
                if (notificationsToRemove == null)
                {
                    scope.Complete();
                    return;
                }
                
                var notifications =
                    from Storage.EntityModels.Notifications n in Storage.StorageManager.Instance.Entities.Notifications
                    where n.ReceiverUserId == user.UserID
                    select n;

                foreach (Notification userNotification in notificationsToRemove)
                {
                    var n = notifications.Where(userN => userN.Id == userNotification.Id).First();

                    if ((from Storage.EntityModels.Notifications others
                             in Storage.StorageManager.Instance.Entities.Notifications
                         where others.MessageId == n.MessageId && others.ReceiverUserId != n.ReceiverUserId
                         select others).Count() == 0)
                        Storage.StorageManager.Instance.Entities.Messages.DeleteOnSubmit(n.Message);
                    Storage.StorageManager.Instance.Entities.Notifications.DeleteOnSubmit(n);
                }
                Storage.StorageManager.Instance.Entities.SubmitChanges();
                scope.Complete();
            }
        }


        /// <summary>
        /// Retrieves from the DataBase all the FriendshipNotifications addressed to the given user.
        /// A FriendshipNotification notifies that the given user added a new friend
        /// to his buddy list, and that such a friend added the user to his own buddy list too.
        /// </summary>
        /// <param name="user">
        /// The user whose the FriendshipNotifications list has to be retrieved.
        /// </param>
        /// <returns>
        /// A list (empty if none are found) containing all the FriendshipNotifications addressed to the provided user.
        /// </returns>
        public static List<FriendshipNotification> getFriendshipNotificationsByUserID(TripLoaUser user)
        {
            List<FriendshipNotification> notifications = new List<FriendshipNotification>();
            List<Storage.EntityModels.Notifications> dbNotifications = null;
            try
            {
                dbNotifications =
                    (from Storage.EntityModels.Notifications n in Storage.StorageManager.Instance.Entities.Notifications
                     where n.Receiver.Id == user.UserID && n.Message.Type == Notification.FRIEND_REQUEST_ACCEPTED
                     select n).ToList();
            }
            catch (ArgumentNullException)
            {
                return notifications;
            }
            foreach (Storage.EntityModels.Notifications n in dbNotifications)
            {
                if (CheckUserNonExistance(user, (int)n.UserId))
                {
                    Storage.StorageManager.Instance.Entities.Messages.DeleteOnSubmit(n.Message);
                    Storage.StorageManager.Instance.Entities.SubmitChanges();
                } else {
                    FriendshipNotification notification = new FriendshipNotification(n.Id, n.ReceiverUserId, (int)n.UserId);
                    if (user.GetProfile().IsFriendOf((int)n.UserId)) notifications.Add(notification);
                    else notification.Delete(user);
                }
            }
            return notifications;
        }

        /// <summary>
        /// Retrieves from the DataBase all not examined (unread) FriendshipRequests addressed to the
        /// given user.
        /// </summary>
        /// <param name="user">
        /// The user whose the FriendshipRequests list has to be retrieved.
        /// </param>
        /// <returns>
        /// A list (empty if none are found) containing all the FriendshipRequests addressed to the provided user.
        /// </returns>
        public static List<FriendshipRequest> getIncomingFriendshipRequestsByUserID(TripLoaUser user)
        {
            List<FriendshipRequest> requests = new List<FriendshipRequest>(); ;
            List<Storage.EntityModels.Notifications> dbNotifications = null;
            try
            {
                dbNotifications =
                    (from Storage.EntityModels.Notifications n in Storage.StorageManager.Instance.Entities.Notifications
                     where n.Receiver.Id == user.UserID && n.Message.Type == Notification.FRIEND_REQUEST
                     select n).ToList();
            }
            catch (ArgumentNullException)
            {
                return requests;
            }

            foreach (Storage.EntityModels.Notifications n in dbNotifications)
            {       
                // if asking user doesn't exist anymore, delete notification
                if (CheckUserNonExistance(user, (int)n.UserId))
                {
                    Storage.StorageManager.Instance.Entities.Messages.DeleteOnSubmit(n.Message);
                    Storage.StorageManager.Instance.Entities.SubmitChanges();
                } else {
                    FriendshipRequest request = new FriendshipRequest(n.Id, (int)n.UserId, n.ReceiverUserId);
                    requests.Add(request);
                }
            }
            return requests;
        }
        
        /// <summary>
        /// Checks if asking user doesn't exist anymore
        /// </summary>
        /// <param name="user">current user</param>
        /// <param name="userId">id of user to check for existence</param>
        /// <returns>true if user doesn't exist anymore, false otherwise</returns>
        private static bool CheckUserNonExistance(TripLoaUser user, int userId)
        {
            string userName = user.GetProfile(userId).Username;
            Storage.EntityModels.User recipient = StorageManager.Instance.GetUserByUserName(userName);

            if (!recipient.BookedIn) return true;
            return false;
        }
        
        #endregion
    }
}
